# -*- mode: python -*-

__revision__ = '$Hg: cache.py,v  2006/09/02 17:40:06 +0200 $'

# ddb imports
from ddb.settings import *

# twisted imports
from twisted.internet import reactor

# system imports
import time

class cache(dict):
    """Cache objects."""

    def __init__(self):
        reactor.callLater(CACHE_INVALIDATE, self.expire)

    def addValue(self, name, record):
        self[name] = {
            'data':   record,
            'expire': time.time() + CACHE_POSITIVE_LIFETIME
            }

    def addList(self, name, *record):
        self[name] = {
            'data':   list(record),
            'expire': time.time() + CACHE_POSITIVE_LIFETIME
            }
        del record # rm ref

    def add(self, name, **record):
        self[name] = {
            'data':   record.copy(),
            'expire': time.time() + CACHE_POSITIVE_LIFETIME
            }
        del record # rm ref

    def expire(self):
        """Remove expired entries from the cache."""
        for record in self.keys():
            if self[record]['expire'] <= time.time():
                del self[record]
        reactor.callLater(60, self.expire)

    def lookup(self, record):
        """Lookup a record in the cache."""
        if self.valid(record):
            return self[record]['data']
        else:
            self[record] = {
                'data':   None,
                'expire': time.time() + CACHE_NEGATIVE_LIFETIME
                }
            return False

    def remove(self, record):
        if self.has_key(record):
            del self[record]

    def valid(self, record):
        """Check if the record is still valid."""
        if not self.has_key(record):
            return False
        elif self[record]['expire'] <= time.time():
            del self[record]
            return False
        else:
            return True

class _cachedCallback(cache):
    def __init__(self):
        cache.__init__(self)
        self.callbacks  = {}

    def addCallback(self, callback):
        """This is also a cache, but when the cache expires it will
        attempt to get a new value via a callback."""
        identifier = '%x' % abs(id(callback))
        self.callbacks[identifier] = callback
        self.refresh(identifier)

    def expire(self):
        """Refresh expired entries from the cache."""
        for record in self.keys():
            if self[record]['expire'] <= time.time():
                self.refresh(record)
        reactor.callLater(60, self.expire)

    def refresh(self, identifier):
        callback = self.callbacks[identifier]
        print "REFRESH", identifier
        print "RUNNING", str(callback)
        callback()
        self.addValue(identifier, True)

cachedCallback = _cachedCallback()
cached = cachedCallback.addCallback

